package com.cupdata.zicon.jdk7concurrent.chapter4;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Since Java 5, the Java concurrency API provides a mechanism that aims at
 * resolving problems. This mechanism is called the Executor framework and is
 * around the Executor interface, its subinterface ExecutorService, and the
 * ThreadPoolExecutor class that implements both interfaces.
 * 
 * This mechanism separates the task creation and its execution. With an
 * executor, you only have to implement the Runnable objects and send them to
 * the executor. It is responsible for their execution, instantiation, and
 * running with necessary threads. But it goes beyond that and improves
 * performance using a pool of threads. When you send a task to the executor, it
 * tries to use a pooled thread for the execution of this task, to avoid
 * continuous spawning of threads.
 * 
 * Another important advantage of the Executor framework is the Callable
 * interface. It's similar to the Runnable interface, but offers two
 * improvements, which are as follows: (1)The main method of this
 * interface,named call(), may return a result. (2) When you send a Callable
 * object to an executor, you get an object that implements the Future
 * interface. You can use this object to control the status and the result of
 * the Callable object.
 * 
 * 
 * The first step to work with the Executor framework is to create an object of
 * the ThreadPoolExecutor class. You can use the four constructors provided by
 * that class or use a factory class named Executors that creates
 * ThreadPoolExecutor. Once you have an executor, you can send Runnable or
 * Callable objects to be executed
 * 
 * you have to end it explicitly.
 * 
 * A Java application won't end until all its non-daemon threads finish their
 * execution, so, if you don't terminate the executor, your application will
 * never end.
 * 
 * @author SunYabing
 *
 */
public class Creating_A_Thread_Executor {
	public static void main(String[] args) {
		Server server = new Server();
		for (int i = 0; i < 100; i++) {
			Task2 task = new Task2("Task " + i);
			server.executeTask(task);
		}
		server.endServer();
	}
}

class Task2 implements Runnable {
	private Date initDate;
	private String name;

	public Task2(String name) {
		initDate = new Date();
		this.name = name;
	}

	@Override
	public void run() {
		System.out.printf("%s: Task %s: Created on: %s\n", Thread
				.currentThread().getName(), name, initDate);
		System.out.printf("%s: Task %s: Started on: %s\n", Thread
				.currentThread().getName(), name, new Date());

		try {
			Long duration = (long) (Math.random() * 10);
			System.out.printf("%s: Task %s: Doing a task during %d seconds\n",
					Thread.currentThread().getName(), name, duration);
			TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.printf("%s: Task %s: Finished on: %s\n", Thread
				.currentThread().getName(), name, new Date());

	}
}

class Server {
	private ThreadPoolExecutor executor;

	public Server() {
		executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();		
	}

	public void executeTask(Task2 task) {
		System.out.printf("Server: A new task has arrived\n");
		executor.execute(task);
		System.out.printf("Server: Pool Size: %d\n", executor.getPoolSize());
		System.out.printf("Server: Active Count: %d\n",
				executor.getActiveCount());
		System.out.printf("Server: Completed Tasks: %d\n",
				executor.getCompletedTaskCount());
	}

	public void endServer() {
		executor.shutdown();
	}
}